package untils

import ( // 引入标准库与第三方库
	"bytes"           // 字节缓冲区，用于构造查询串
	"crypto/hmac"     // HMAC 签名算法
	"crypto/sha1"     // SHA1 哈希
	"encoding/base64" // base64 编码
	"encoding/json"   // JSON 编解码
	"errors"          // 错误包装
	"fmt"             // 格式化打印
	"io"              // IO 工具
	"net/http"        // HTTP 客户端
	"net/url"         // URL 编码
	"sort"            // 排序，用于字典序拼接参数
	"strings"         // 字符串处理
	"time"            // 时间戳与超时
)

const ( // 常量区：阿里云 CloudAuth 接口信息
	aliGateway = "https://cloudauth.aliyuncs.com" // 云市场网关地址
	aliVersion = "2022-11-14"                     // API 版本号
	aliAction  = "VerifyDriverLicense"            // 具体接口 Action 名称
)

// AliyunClient 封装阿里云 CloudAuth 的调用细节
type AliyunClient struct {
	AccessKey string       // 阿里云 AccessKey ID
	Secret    string       // 阿里云 AccessKey Secret
	hc        *http.Client // 内置 HTTP 客户端，带超时
}

// NewAliyunClient 构造函数，返回一个已初始化好的客户端指针
func NewAliyunClient(ak, sk string) *AliyunClient {
	return &AliyunClient{ // 取地址返回结构体指针
		AccessKey: ak,                                     // 保存 AK
		Secret:    sk,                                     // 保存 SK
		hc:        &http.Client{Timeout: 5 * time.Second}, // 默认 5 秒超时
	}
}

// VerifyDriverLicense 调用阿里云「驾驶证信息核验」接口
func (c *AliyunClient) VerifyDriverLicense(name, driverNo, archiveNo string) (*AliyunVerifyResp, error) {
	params := make(map[string]string)                                     // 初始化参数字典
	params["RegionId"] = "cn-hangzhou"                                    // 固定区域
	params["Action"] = aliAction                                          // 接口名
	params["Version"] = aliVersion                                        // 版本
	params["Timestamp"] = time.Now().UTC().Format("2006-01-02T15:04:05Z") // UTC 时间戳
	params["Format"] = "JSON"                                             // 返回格式
	params["AccessKeyId"] = c.AccessKey                                   // AK
	params["SignatureMethod"] = "HMAC-SHA1"                               // 签名算法
	params["SignatureVersion"] = "1.0"                                    // 签名版本
	params["SignatureNonce"] = fmt.Sprintf("%d", time.Now().UnixNano())   // 随机串防重放

	// 业务参数
	params["Name"] = name                // 持证人姓名
	params["DriverLicenseNo"] = driverNo // 驾驶证号
	params["ArchiveNo"] = archiveNo      // 档案编号

	// 1. 按键字典序排序
	var keys []string       // 临时切片保存所有 key
	for k := range params { // 遍历 map
		keys = append(keys, k) // 追加到切片
	}
	sort.Strings(keys) // 字典序排序

	// 2. 构造 CanonicalQueryString
	var pairs []string       // 保存 key=value 对
	for _, k := range keys { // 按排序后顺序遍历
		pairs = append(pairs, percentEncode(k)+"="+percentEncode(params[k])) // URL 编码并拼接
	}
	cqs := strings.Join(pairs, "&") // 用 & 连接

	// 3. 构造签名字符串
	stringToSign := "GET&" + percentEncode("/") + "&" + percentEncode(cqs) // 固定 GET 方法 + 路径 + 参数
	sign := signHMAC(c.Secret+"&", stringToSign)                           // 用 SK 做 HMAC-SHA1 并 base64
	params["Signature"] = sign                                             // 把签名放回参数

	// 4. 发起 GET 请求
	rawURL := aliGateway + "/?" + buildQuery(params) // 拼最终带参 URL
	req, _ := http.NewRequest("GET", rawURL, nil)    // 构造 GET 请求
	resp, err := c.hc.Do(req)                        // 正式发送
	if err != nil {                                  // 网络层错误
		return nil, err // 向上抛
	}
	defer resp.Body.Close()               // 记得关闭响应体
	body, _ := io.ReadAll(resp.Body)      // 一次性读取全部
	if resp.StatusCode != http.StatusOK { // 非 200 说明 HTTP 异常
		return nil, fmt.Errorf("aliyun http %d: %s", resp.StatusCode, string(body))
	}

	// 5. 解析 JSON
	var result AliyunVerifyResp                           // 定义接收结构
	if err := json.Unmarshal(body, &result); err != nil { // 反序列化
		return nil, err // 解析失败返回
	}
	if result.Code != "OK" { // 业务错误
		return nil, errors.New(result.Message) // 返回官方错误描述
	}
	return &result, nil // 成功返回结构体指针
}

// AliyunVerifyResp 阿里云返回的 JSON 对应结构
type AliyunVerifyResp struct {
	Code      string   `json:"Code"`      // 返回码：OK 表示成功
	Message   string   `json:"Message"`   // 返回描述
	RequestId string   `json:"RequestId"` // 请求唯一 ID，排障用
	Data      struct { // 业务数据嵌套
		Name            string `json:"Name"`            // 姓名
		DriverLicenseNo string `json:"DriverLicenseNo"` // 驾驶证号
		ArchiveNo       string `json:"ArchiveNo"`       // 档案编号
		Status          string `json:"Status"`          // 公安状态：正常、暂扣、吊销等
		VehicleType     string `json:"VehicleType"`     // 准驾车型：A1/A2/B1/B2/C1…
		ValidFrom       string `json:"ValidFrom"`       // 有效起始日期
		ValidTo         string `json:"ValidTo"`         // 有效截止日期
	} `json:"Data"`
}

// --------- 以下 3 个是纯工具函数 ---------

// percentEncode 对字符串做 RFC3986 编码
func percentEncode(s string) string {
	s = url.QueryEscape(s)                // 先走标准库
	s = strings.ReplaceAll(s, "+", "%20") // 空格变 %20
	s = strings.ReplaceAll(s, "*", "%2A") // * 号特殊处理
	s = strings.ReplaceAll(s, "%7E", "~") // ~ 不编码
	return s
}

// signHMAC 用 key 对 data 做 HMAC-SHA1 并 base64 编码
func signHMAC(key, data string) string {
	h := hmac.New(sha1.New, []byte(key))                 // 新建 HMAC，哈希算法 SHA1
	h.Write([]byte(data))                                // 写入待签名字节
	return base64.StdEncoding.EncodeToString(h.Sum(nil)) // 返回 base64
}

// buildQuery 把 map 拼成 URL 查询串（不编码，前面已编码）
func buildQuery(m map[string]string) string {
	var buf bytes.Buffer  // 字节缓冲区
	for k, v := range m { // 遍历 map
		if buf.Len() > 0 { // 非第一个参数前加 &
			buf.WriteByte('&')
		}
		buf.WriteString(k) // 写 key
		buf.WriteByte('=') // 写 =
		buf.WriteString(v) // 写 value
	}
	return buf.String() // 返回字符串
}
